package Control;

import View.InterfaceVista;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.StringTokenizer;
import java.util.Vector;

import algoritmos.*;
import matrices.centro.Clase;
import problema.FormaProblema;

/**
 * 
 * @author  Jose Antonio Gonzalez,Ambrin Chaudhary
 *
 */
public class Controlador implements InterfaceControl {

    private FormaProblema problema = null;
    private InterfaceVista vista;
    //variable que indicará si se ha realizado el ajuste
    protected boolean ajustado = false;
    //guardamos los ficheros que introduzca el usuario
    protected File fichACuantizar = null;
    protected File fichCuantizado = null;
    private CV cv;

    public Controlador() {
        // El constructor de control debe estar vac�o.
    }

    /**
     * Inicializa la Vista 
     */
    public void registerView(InterfaceVista vista) {
        this.vista = vista;
    }

    /**
     * Método que procesa el fichero para cuantizar que ha cargado el usuario
     *
     * @param fichero
     */
    public void cargarFichACuantizar(File fichero) {
        BufferedReader inStream = null;
        try {

            inStream = new BufferedReader(new FileReader(fichero));

            if (this.problema == null) {
                this.vista.muestraError("No hay ningún algoritmo seleccionado");
            } else {
                this.problema.cargarFichero(inStream);
                this.fichACuantizar = fichero;

                this.vista.muestraResultado(this.problema.getClases());
            }


            System.out.println("===================Fichero a cuantizar cargado=================== ");

        } catch (FileNotFoundException ex) {
            vista.muestraError("Fichero no encontrado");
        } catch (IOException e) {
            vista.muestraError("Error de I/O al cagar el archivo");
        } finally {
            try {
                inStream.close();
            } catch (IOException ex) {
                vista.muestraError("Error no esperado en controlador.cargarFicheroDeAprendizaje");
            }
        }
    }

    /**
     * Metodo que procesa el fichero cuantizado que ha introducido el usuario
     * (Se trata de uno de cuantizacion vectorial)
     * @param fichero
     */
    public void cargarFichCuantizado(File fichero) {

        BufferedReader inStream = null;
        try {

            inStream = new BufferedReader(new FileReader(fichero));

            this.fichCuantizado = fichero;

            cv = new CV();
            cv.cargarFichACuantizar(inStream);
            cv.cuantizar();
            //en CV ya tenemos el vector de clases y muestras

            //mostramos en la pantalla grafica
            this.vista.muestraResultado(cv.getClases());
            System.out.println("===================Fichero cuantizado cargado===================");

        } catch (FileNotFoundException ex) {
            vista.muestraError("Fichero no encontrado");
        } catch (IOException e) {
            vista.muestraError("Error de I/O al cagar el archivo");
        } catch (Exception e) {
            this.vista.muestraError("Fichero no válido");
        } finally {
            try {
                inStream.close();
            } catch (IOException ex) {
                vista.muestraError("Error no esperado en controlador.cargarFicheroDeAprendizaje");
            }
        }
    }

    /**
     * Ajusta los datos del fichero según el algoritmo seleccionado por el usuario
     * Previamente se comprueba que se haya cargado un fichero
     */
    public void ajustar(Vector<Double> parametros) {

        //comprobar que se haya introducido un fichero
        if (esPosibleAjustar()) {

            //Comprobaciones: Si se ha cargado un fichero cuantizado, pero no el de cuantizar, se pueden utilizar los datos de cv
            if (this.fichACuantizar == null) {
                if (this.fichCuantizado == null) {
                    //error: no ha cargado ningun fichero. 
                    this.vista.muestraError("Antes de ajustar, debe cargar un fichero");
                } else {
                    try {
                        this.problema.cargarClases(cv.getClases(), cv.getMuestras());
                        this.problema.entrenar(parametros);
                    } catch (Exception e) {
                        System.err.println("Error al ajustar. Problema esta a null?? " + e.getMessage());
                    }
                }
            } else {
                if (this.problema == null) {
                    System.err.println("ERROR SEVERO: problema está a null pero hay un fichero para cuantizar");
                } else {
                    this.problema.entrenar(parametros);
                //Ya hemos hecho la divergencia, ahora se puede evaluar
                }
            }
            this.ajustado = true;

            //muestro los datos en la interfaz gráfica
            this.vista.muestraResultado(this.problema.getClases());

            System.out.println("===================Ajuste realizado===================");
        }

    }

    /**
     * 
     * Realiza el ciclo a partir de los parámetros que el usuario haya 
     * @param parametros para la evaluación
     */
    public void evaluar(String parametros) {

        if (esPosibleEvaluar()) {

            Vector<Double> vParams = new Vector<Double>();
            try {

                StringTokenizer stokens = new StringTokenizer(parametros, ";");

                //crear un vector de doubles con los tokens obtenidos
                while (stokens.hasMoreTokens()) {

                    String sigToken = stokens.nextToken();
                    System.err.println("token " + sigToken);
                    vParams.add(Double.valueOf(sigToken));

                }

                if (vParams.size() != this.problema.longVect()) {
                    this.vista.muestraError("Faltan parámetros. Debe especificar " + this.problema.longVect());
                } else {
                    Vector<Clase> clases = new Vector<Clase>();
                    clases.add(this.problema.ciclo(vParams));

                    this.vista.muestraResultado(clases);
                    System.out.println("===================Evaluación realizada===================");
                }



            } catch (Exception e) {
                this.vista.muestraError("Error mientras se procesaban los parámetros. " + e.getMessage());
                e.printStackTrace();

            }


        } else {
            //error: no se ha cargado un fichero ya aprendido
            this.vista.muestraError("Por favor, ajuste los datos");

        }

    }

    /**
     * Determina si es posible llevar a cabo la evaluación, para lo cual se debe haber realizado
     * la divergencia (Ajuste)     
     * 
     * @return true si se puede evaluar, false en otro caso
     */
    public boolean esPosibleEvaluar() {

        if (!this.ajustado) {
            this.vista.muestraError("Por favor, ajuste los datos");
            return false;
        }

        return true;

    }

    /**
     * Determina si es posible llevar a cabo el ajuste, para lo cual 
     * se debe haber cargado un fichero, o bien haber cargado uno de 
     * cuantización vectorial
     * 
     * @return true si se puede ajustar, false en otro caso
     */
    public boolean esPosibleAjustar() {
        if (this.fichCuantizado == null && this.fichACuantizar == null) {

            this.vista.muestraError("Por favor, cargue un fichero");
            return false;
        }

        return true;
    }

    /**
     * Informa al controlador del algoritmo que el usuario ha seleccionado
     * 
     * @param i : indice del algoritmo 
     */
    public void setAlgoritmo(int i) {

        //********************************************
//        this.fichACuantizar = null;
//        this.ajustado = false;
        //********************************************

        switch (i) {
            case 1://

            case 2://algoritmo de k medias

                //this.problema = this.problemaKMedias;
                this.problema = new KMeans();

                break;
            case 3://añgoritmo de Lloyd

                // this.problema = this.problemaLloyd;
                this.problema = new Lloyd();

                break;

            case 4://algoritmo de SOM

                //this.problema = this.problemaSOM;
                this.problema = new SOM();

                break;

            default://error, el algoritmo no esta declarado

                System.err.println("error: algoritmo no definido: " + i);

        }

    }

    public void reiniciar() {
        this.fichACuantizar = null;
        this.ajustado = false;
        this.problema=null;
        
        cv=null;
    }
}
